Line Save File to Google Drive and Log File's URL

工作流概述

这是一个包含27个节点的复杂工作流,主要用于自动化处理各种任务。

工作流源代码

下载
{
  "id": "mqdP7Aw1KnkIq2W5",
  "meta": {
    "instanceId": "2c12b0b552404dc07af67cd5f092afd21d18c808d4fdabdb04cb4b064195b6fb",
    "templateCredsSetupCompleted": true
  },
  "name": "Line Save File to Google Drive and Log File's URL",
  "tags": [
    {
      "id": "W3ZSaJHRI2hXA9gT",
      "name": "Line Messaging API",
      "createdAt": "2025-03-09T13:14:42.780Z",
      "updatedAt": "2025-03-09T13:14:42.780Z"
    }
  ],
  "nodes": [
    {
      "id": "47c9f83b-5590-4ffc-825c-5fee72e8ef87",
      "name": "Get Config",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1220,
        -200
      ],
      "parameters": {
        "range": "config!A1:H2",
        "options": {},
        "sheetId": "1iO4ZHU7s0fe1Jn8jcScNDce7rFXQlkRBqsO8IFHbcSc"
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "0RVWjnYzlWor2bMu",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 2,
      "alwaysOutputData": true
    },
    {
      "id": "1514cec1-bd12-4ce4-99af-bd2465026822",
      "name": "Create Date Folder",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        1700,
        80
      ],
      "parameters": {
        "name": "={{ $('Determine Folder Info').item.json.dateFolderName }}",
        "options": {
          "parents": [
            "={{ $('Determine Folder Info').item.json.baseFolderId }}"
          ]
        },
        "resource": "folder"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "id": "QVrgALkld7whKIgB",
          "name": "Google Drive account - Peakwave"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "a584238e-1d53-46ff-8cfc-437e14ea71d9",
      "name": "Set Date Folder ID",
      "type": "n8n-nodes-base.code",
      "position": [
        1960,
        -20
      ],
      "parameters": {
        "jsCode": "// Log ข้อมูล input ทั้งหมด
//console.log(\"Set Target Parent (Date) - Input:\", items);

let targetParentId = '';

if (items.length > 0) {
	// ตรวจสอบจาก branch แรก (True Branch จาก IF node)
	if (items[0].json && items[0].json.id) {
		targetParentId = items[0].json.id;
	} else if (items.length > 1 && items[1].json && items[1].json.id) {
		// ถ้าไม่พบจาก branch แรก ให้ลองดูจาก branch ที่สอง (False Branch หรือผลจากการสร้าง folder ใหม่)
		targetParentId = items[1].json.id;
	}
	
	// เพิ่ม targetParentId ลงใน items[0].json
	items[0].json.targetParentId = targetParentId;
	console.log(\"Set Target Parent (Date) - Output:\", items);
	return items;
} else {
	console.log(\"Set Target Parent (Date) - No input items.\");
	return [];
}
"
      },
      "typeVersion": 2,
      "alwaysOutputData": false
    },
    {
      "id": "aa480dc9-935e-4a6e-a6c6-2559255ae1c2",
      "name": "Create File Type Folder",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        1700,
        400
      ],
      "parameters": {
        "name": "={{ $('Determine Folder Info').item.json.fileTypeFolderName }}",
        "driveId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Determine Folder Info').item.json.baseFolderId }}"
        },
        "options": {},
        "folderId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Get Config').first().json['Store by Date'] === true ? $('Set Date Folder ID').first().json.targetParentId : $('Get Config').first().json[\"Parent Folder ID\"] }}"
        },
        "resource": "folder"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "id": "QVrgALkld7whKIgB",
          "name": "Google Drive account - Peakwave"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "d5ed8981-5b65-45a3-8ff4-c00902756bb9",
      "name": "Upload File to Google Drive",
      "type": "n8n-nodes-base.googleDrive",
      "onError": "continueRegularOutput",
      "position": [
        1600,
        640
      ],
      "parameters": {
        "name": "={{ $('Merge Event and Config Data').item.json.body.events[0].timestamp }}.{{$node[\"Get File Binary Content\"].binary.data.fileExtension}}",
        "driveId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Configure Final Parent Folder ID').item.json.finalParentId }}"
        },
        "options": {},
        "folderId": {
          "__rl": true,
          "mode": "id",
          "value": "root"
        }
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "id": "QVrgALkld7whKIgB",
          "name": "Google Drive account - Peakwave"
        }
      },
      "typeVersion": 3,
      "alwaysOutputData": false
    },
    {
      "id": "92787455-6839-455f-a939-6927660bc215",
      "name": "Determine Folder Info",
      "type": "n8n-nodes-base.code",
      "position": [
        1960,
        -280
      ],
      "parameters": {
        "jsCode": "const data = items[0].json;
const config = data.config;
const event = data.event;

// ใช้ key จาก config ตามที่ส่งมา
let baseFolderId = config[\"Parent Folder ID\"];
let dateFolderName = \"\";
let fileTypeFolderName = \"\";

// หากตั้งค่า Store by Date เป็น true
if (config[\"Store by Date\"]) {
  // ใช้ CurrentDate จาก config หรือใช้วันที่ปัจจุบันถ้าไม่มี
  dateFolderName = config[\"CurrentDate\"] ? config[\"CurrentDate\"] : new Date().toISOString().slice(0,10).replace(/-/g, \"\");
}

// หากตั้งค่า Store by File Type เป็น true
if (config[\"Store by File Type\"]) {
  // ตรวจสอบว่า event.body.events มีข้อมูลหรือไม่
  if (event.body && event.body.events && event.body.events.length > 0) {
    // ดึง type ของ message จาก event.body.events[0]
    fileTypeFolderName = event.body.events[0].message.type.toLowerCase();
  }
}

return [{
  json: {
    baseFolderId,
    dateFolderName,
    fileTypeFolderName,
    storeByDate: config[\"Store by Date\"],
    storeByFileType: config[\"Store by File Type\"],
    event: event,
    config: config
  }
}];
"
      },
      "typeVersion": 2
    },
    {
      "id": "f4e30758-986e-4dd4-bc85-f2953e883bfe",
      "name": "Search Date Folder",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        980,
        0
      ],
      "parameters": {
        "filter": {
          "folderId": {
            "__rl": true,
            "mode": "id",
            "value": "={{ $json.baseFolderId }}"
          }
        },
        "options": {},
        "resource": "fileFolder",
        "queryString": "={{ $json.dateFolderName }}"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "id": "QVrgALkld7whKIgB",
          "name": "Google Drive account - Peakwave"
        }
      },
      "typeVersion": 3,
      "alwaysOutputData": true
    },
    {
      "id": "e74d65bc-be7f-43d0-8b0a-ee6316b4aff9",
      "name": "Merge Event and Config Data",
      "type": "n8n-nodes-base.merge",
      "position": [
        1480,
        -280
      ],
      "parameters": {
        "mode": "mergeByIndex"
      },
      "typeVersion": 1
    },
    {
      "id": "f380d584-32aa-435f-8e8e-6d1d05932bd2",
      "name": "Check Existing Date Folder",
      "type": "n8n-nodes-base.if",
      "position": [
        1220,
        0
      ],
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $json.id !== undefined }}",
              "value2": true
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "96dc0853-67ec-4cfc-b40c-0cce63f51e96",
      "name": "Check Existing File Type Folder",
      "type": "n8n-nodes-base.if",
      "position": [
        1220,
        320
      ],
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $json.id !== undefined }}",
              "value2": true
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "742cb154-3b23-423d-8df6-53a82f0d8aab",
      "name": "Merge Final Parent Folder Data",
      "type": "n8n-nodes-base.merge",
      "position": [
        2280,
        400
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "1003b29d-9140-4961-90d4-b94e4d33dc69",
      "name": "Search File Type Folder",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        980,
        320
      ],
      "parameters": {
        "filter": {
          "folderId": {
            "__rl": true,
            "mode": "id",
            "value": "={{ $('Get Config').item.json['Store by Date'] === true && $json.targetParentId && $json.targetParentId !== \"\" ? $json.targetParentId : $('Get Config').item.json['Parent Folder ID'] }}"
          }
        },
        "options": {},
        "resource": "fileFolder",
        "queryString": "={{ $('Determine Folder Info').item.json.fileTypeFolderName }}"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "id": "QVrgALkld7whKIgB",
          "name": "Google Drive account - Peakwave"
        }
      },
      "typeVersion": 3,
      "alwaysOutputData": true
    },
    {
      "id": "8015eb75-0c38-4c3b-a29f-f04dedf79cee",
      "name": "Set File Type Folder ID",
      "type": "n8n-nodes-base.code",
      "position": [
        1960,
        320
      ],
      "parameters": {
        "jsCode": "// Log ข้อมูล input ทั้งหมด
//console.log(\"Set Target Parent (Date) - Input:\", items);

let targetParentId = '';

if (items.length > 0) {
	// ตรวจสอบจาก branch แรก (True Branch จาก IF node)
	if (items[0].json && items[0].json.id) {
		targetParentId = items[0].json.id;
	} else if (items.length > 1 && items[1].json && items[1].json.id) {
		// ถ้าไม่พบจาก branch แรก ให้ลองดูจาก branch ที่สอง (False Branch หรือผลจากการสร้าง folder ใหม่)
		targetParentId = items[1].json.id;
	}
	
	// เพิ่ม targetParentId ลงใน items[0].json
	items[0].json.targetParentId = targetParentId;
	console.log(\"Set Target Parent (Date) - Output:\", items);
	return items;
} else {
	console.log(\"Set Target Parent (Date) - No input items.\");
	return [];
}
"
      },
      "typeVersion": 2
    },
    {
      "id": "b59d58c7-cb8c-4edc-a7e7-533675c39a96",
      "name": "Configure Final Parent Folder ID",
      "type": "n8n-nodes-base.code",
      "position": [
        960,
        640
      ],
      "parameters": {
        "jsCode": "/**
 * Expected input: items array จาก Merge Final Data
 * - items[0].json คือผลลัพธ์ที่อาจเป็น folder สำหรับวันที่หรือ file type folder (ขึ้นอยู่กับเงื่อนไข)
 * - items[1].json คือผลลัพธ์อีกส่วนหนึ่ง (สำหรับกรณีที่มีทั้งสอง)
 * 
 * Config จะถูกดึงมาจาก node 'Get Config'
 */

const config = $('Get Config').first().json;
let finalParentId = '';

// เงื่อนไขเลือก finalParentId
if (config[\"Store by Date\"] === true && config[\"Store by File Type\"] === true) {
    // เมื่อทั้งสองเป็น TRUE: สมมุติว่า file type folder อยู่ใน items[1]
    finalParentId = $('Set File Type Folder ID').first().json.targetParentId
} else if (config[\"Store by Date\"] === true && config[\"Store by File Type\"] === false) {
    // ใช้ folder ตามวันที่ (items[0])
    finalParentId =$('Set Date Folder ID').first().json.targetParentId;
} else if (config[\"Store by Date\"] === false && config[\"Store by File Type\"] === true) {
    // ใช้ folder สำหรับประเภทไฟล์ (ใน test case นี้ ใช้ items[0])
    finalParentId = $('Set File Type Folder ID').first().json.targetParentId;
} else {
    // เมื่อทั้งสองเป็น FALSE: ใช้ Parent Folder ID จาก config
    finalParentId = config[\"Parent Folder ID\"];
}

// เพิ่ม finalParentId ลงใน items[0].json เพื่อส่งต่อให้ Node \"Upload File to Google Drive\" ใช้งาน
items[0].json.finalParentId = finalParentId;
return [items[0]];
"
      },
      "typeVersion": 2
    },
    {
      "id": "683c83fc-e4b7-4a0d-b5f0-8958d0743faf",
      "name": "Process Event and Config Data",
      "type": "n8n-nodes-base.code",
      "position": [
        1680,
        -280
      ],
      "parameters": {
        "jsCode": "// ตรวจสอบว่า items มีอย่างน้อย 2 รายการหรือไม่
const eventData = items[0].json;
let config;

if (items.length >= 2) {
	// ถ้ามี items[1] ให้ใช้เป็น config
	const configData = items[1].json;
	config = Array.isArray(configData) ? configData[0] : configData;
} else {
	// ถ้าไม่มี items[1] ให้ลองดึง config จาก eventData
	// สมมุติว่าฟิลด์ config ถูกส่งมาพร้อมกับ eventData ด้วยชื่อฟิลด์เหมือนที่ได้ใน output
	if (eventData[\"Parent Folder Path\"] && eventData[\"Parent Folder ID\"]) {
		config = {
			\"Parent Folder Path\": eventData[\"Parent Folder Path\"],
			\"Parent Folder ID\": eventData[\"Parent Folder ID\"],
			\"Store by Date\": eventData[\"Store by Date\"],
			\"Store by File Type\": eventData[\"Store by File Type\"],
			\"Allow File Types\": eventData[\"Allow File Types\"],
			\"CurrentDate\": eventData[\"CurrentDate\"],
			\"Reply Enabled\": eventData[\"Reply Enabled\"],
			\"CHANNEL ACCESS TOKEN\": eventData[\"CHANNEL ACCESS TOKEN\"]
		};
	} else {
		throw new Error(\"ไม่พบข้อมูล config! กรุณาตรวจสอบว่า node ก่อนหน้านี้ส่งข้อมูล config มาด้วย\");
	}
}

return [{ json: { event: eventData, config: config } }];
"
      },
      "typeVersion": 2
    },
    {
      "id": "fb5bd15c-bb0a-420b-a14b-ed5df5ecd691",
      "name": "Get File Binary Content",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1180,
        640
      ],
      "parameters": {
        "url": "=https://api-data.line.me/v2/bot/message/{{ $('LINE Webhook Listener').item.json.body.events[0].message.id }}/content",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "id": "byY3kI23lMe4ewnM",
          "name": "Header Auth account - Maid"
        }
      },
      "executeOnce": true,
      "typeVersion": 4.2
    },
    {
      "id": "f064ba27-a5a8-4cca-afb8-3e099fb5abc8",
      "name": "Log File Details to Google Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "onError": "continueRegularOutput",
      "position": [
        1820,
        640
      ],
      "parameters": {
        "columns": {
          "value": {
            "File Name": "={{ $json.name }}",
            "File Type": "={{ $json.fileExtension }}",
            "Date Uploaded": "={{ $json.createdTime }}",
            "Google Drive URL": "={{ $json.webContentLink }}"
          },
          "schema": [
            {
              "id": "File Name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "File Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Date Uploaded",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Date Uploaded",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Google Drive URL",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Google Drive URL",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "File Type",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "File Type",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 585160829,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1iO4ZHU7s0fe1Jn8jcScNDce7rFXQlkRBqsO8IFHbcSc/edit#gid=585160829",
          "cachedResultName": "fileList"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "1iO4ZHU7s0fe1Jn8jcScNDce7rFXQlkRBqsO8IFHbcSc"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "0RVWjnYzlWor2bMu",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.5,
      "alwaysOutputData": true
    },
    {
      "id": "fd936998-6ba0-4dbe-b406-c785f89181dd",
      "name": "Check Reply Enabled Flag",
      "type": "n8n-nodes-base.if",
      "position": [
        2040,
        640
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "8f593e3a-95dd-457e-903f-f2ca68cdbcd1",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $('Get Config').item.json['Reply Enabled'] }}",
              "rightValue": "true"
            }
          ]
        }
      },
      "typeVersion": 2.2,
      "alwaysOutputData": true
    },
    {
      "id": "7d1f19e7-cb6c-4f5d-8a10-b84e9e06345a",
      "name": "Check if Store by Date is Enabled",
      "type": "n8n-nodes-base.if",
      "position": [
        1420,
        80
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "370bcd8c-c72a-4e69-acfd-9e271b1a09ed",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $('Get Config').item.json['Store by Date'] === true }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "8644134e-673c-4360-8831-71c048c1522d",
      "name": "Check if Store by File Type is Enabled",
      "type": "n8n-nodes-base.if",
      "position": [
        1420,
        400
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "7c0577ba-b2ed-4050-a580-3cadc7da2b73",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $('Get Config').item.json['Store by File Type'] === true }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "f6b24396-a854-42c4-ae70-55095d637b9a",
      "name": "LINE Webhook Listener",
      "type": "n8n-nodes-base.webhook",
      "position": [
        980,
        -300
      ],
      "webhookId": "feb869e5-a96c-4a5c-b346-3d7c7e64bf0a",
      "parameters": {
        "path": "line-webhook",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 1
    },
    {
      "id": "3b1ca174-f5fc-4b01-90f4-be9240f8be77",
      "name": "Send LINE Reply Message",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueRegularOutput",
      "position": [
        2280,
        620
      ],
      "parameters": {
        "url": "https://api.line.me/v2/bot/message/reply",
        "method": "POST",
        "options": {},
        "jsonBody": "={
  \"replyToken\": \"{{ $('LINE Webhook Listener').first().json.body.events[0].replyToken }}\",
  \"messages\": [
    {
      \"type\": \"text\",
      \"text\": \"{{ $('Validate File Type').item.json.error ? $('Validate File Type').item.json.error : $json['Google Drive URL'] }}\"
    }
  ]
}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "id": "byY3kI23lMe4ewnM",
          "name": "Header Auth account - Maid"
        }
      },
      "typeVersion": 4.2,
      "alwaysOutputData": true
    },
    {
      "id": "87465dac-4557-4ee4-ae21-b36aab37c884",
      "name": "Validate File Type",
      "type": "n8n-nodes-base.code",
      "onError": "continueRegularOutput",
      "position": [
        1380,
        640
      ],
      "parameters": {
        "jsCode": "// ดึงค่า allowed types จาก Node \"Get Config\"
const allowedTypes = $('Get Config').first().json[\"Allow File Types\"]
  .split(\"|\")
  .map(s => s.trim().toLowerCase());

// ดึงค่า file type จากข้อมูลของ event (ปรับให้ตรงกับ structure ของข้อมูลคุณ)
const fileType = $('LINE Webhook Listener').first().json.body.events[0].message.type.toLowerCase();

// ตรวจสอบว่า fileType อยู่ใน allowedTypes หรือไม่
if (!allowedTypes.includes(fileType)) {
  // สร้าง Error object พร้อมแนบข้อมูล replyToken และ errorMessage
  const error = new Error(`File type '${fileType}' is not allowed.`);
  error.json = {
    replyToken: $('LINE Webhook Listener').first().json.body.events[0].replyToken,
    errorMessage: error.message,
  };
  throw error;
}

return items;
"
      },
      "typeVersion": 2,
      "alwaysOutputData": true
    },
    {
      "id": "00ddc2ae-4782-4079-947e-2fda99c0f037",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        220,
        -380
      ],
      "parameters": {
        "width": 2320,
        "height": 320,
        "content": "## Workflow Entry & Configuration
This section initializes the workflow by listening to incoming requests from 
the LINE Messaging API and retrieving configuration details from Google Sheets. 
It merges the event data with the config, then determines initial folder information 
(such as whether to store files by date or file type). Nodes in this group:

* **LINE Webhook Listener**
Receives POST requests (file messages) from LINE.
* **Get Config**
Reads configuration data (parent folder, allowed file types, etc.) from a Google Sheet.
* **Merge Event and Config Data**
Combines the LINE event data and config data.
* **Determine Folder Info**
Calculates folder names based on the config (e.g., date folder name, file type folder name)."
      },
      "typeVersion": 1
    },
    {
      "id": "ccae9a3d-0a46-4c06-b94b-c3bd08d10df1",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        220,
        -40
      ],
      "parameters": {
        "color": 5,
        "width": 2320,
        "height": 340,
        "content": "## Folder Search & Creation
This section handles the logic for finding or creating the appropriate Google Drive folders.
It checks if a date folder exists (when Store by Date is enabled) and whether a file type folder
 is required (when Store by File Type is enabled). Nodes in this group:

* **Search Date Folder / Check Existing Date Folder / Check if Store by Date is Enabled** 
Looks for or creates a date-based folder.
* **Create Date Folder / Set Date Folder ID** 
reates and stores the date folder ID if it doesn’t exist.
* **Search File Type Folder / Check Existing File Type Folder / Check if Store by File Type is Enabled** 
Similarly handles file type subfolder logic.
* **Create File Type Folder / Set File Type Folder ID** 
 Creates and stores the file type folder ID.
* **Merge Final Parent Folder Data / Configure Final Parent Folder ID** 
Merges the final folder IDs (date folder + file type folder) to determine where the file should be placed."
      },
      "typeVersion": 1
    },
    {
      "id": "b9272975-3e0d-436c-b8e3-98fcb8cfc893",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        220,
        600
      ],
      "parameters": {
        "color": 4,
        "width": 2320,
        "height": 320,
        "content": "## Upload, Log, & Reply
Once the file is validated and the correct folder determined, the workflow uploads the file
to Google Drive and logs the details in a Google Sheet. Finally, it checks whether replies 
are enabled and, if so, sends a message back to the LINE user (either confirming a successful
upload or reporting an error). Nodes in this group:

* **Upload File to Google Drive**
Uploads the validated file to the determined folder path.
* **Log File Details to Google Sheet**
Records the file name, upload date, URL, and file type in Google Sheets.
* **Check Reply Enabled Flag**
Verifies if replies to LINE are turned on in the config.
* **Send LINE Reply Message**
Sends a text reply back to the user via LINE, either containing the file’s Google Drive URL or an error message."
      },
      "typeVersion": 1
    },
    {
      "id": "85a84d38-6992-439d-bdaa-de37b0dac554",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        220,
        320
      ],
      "parameters": {
        "color": 6,
        "width": 2320,
        "height": 260,
        "content": "## File Retrieval & Validation
In this group, the workflow fetches the binary content of the file from the LINE API
and validates whether the file type is allowed (e.g., image, audio, video). If the 
file type is not permitted, the workflow throws an error which will be used to send
an appropriate reply message back to LINE. Nodes in this group:

* **Get File Binary Content**
Retrieves the actual file data from the LINE Messaging API.
* **Validate File Type**
Checks the file’s MIME type against an allowed list from the config and throws an error if disallowed."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "10f96ecd-2a9e-48c6-91f6-b49c00e0ac90",
  "connections": {
    "Get Config": {
      "main": [
        [
          {
            "node": "Merge Event and Config Data",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Create Date Folder": {
      "main": [
        [
          {
            "node": "Set Date Folder ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search Date Folder": {
      "main": [
        [
          {
            "node": "Check Existing Date Folder",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Date Folder ID": {
      "main": [
        [
          {
            "node": "Search File Type Folder",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge Final Parent Folder Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate File Type": {
      "main": [
        [
          {
            "node": "Upload File to Google Drive",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Determine Folder Info": {
      "main": [
        [
          {
            "node": "Search Date Folder",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "LINE Webhook Listener": {
      "main": [
        [
          {
            "node": "Merge Event and Config Data",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create File Type Folder": {
      "main": [
        [
          {
            "node": "Set File Type Folder ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get File Binary Content": {
      "main": [
        [
          {
            "node": "Validate File Type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search File Type Folder": {
      "main": [
        [
          {
            "node": "Check Existing File Type Folder",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set File Type Folder ID": {
      "main": [
        [
          {
            "node": "Merge Final Parent Folder Data",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Check Reply Enabled Flag": {
      "main": [
        [
          {
            "node": "Send LINE Reply Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Existing Date Folder": {
      "main": [
        [
          {
            "node": "Set Date Folder ID",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Check if Store by Date is Enabled",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Event and Config Data": {
      "main": [
        [
          {
            "node": "Process Event and Config Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload File to Google Drive": {
      "main": [
        [
          {
            "node": "Log File Details to Google Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Event and Config Data": {
      "main": [
        [
          {
            "node": "Determine Folder Info",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Final Parent Folder Data": {
      "main": [
        [
          {
            "node": "Configure Final Parent Folder ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Existing File Type Folder": {
      "main": [
        [
          {
            "node": "Set File Type Folder ID",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Check if Store by File Type is Enabled",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Configure Final Parent Folder ID": {
      "main": [
        [
          {
            "node": "Get File Binary Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log File Details to Google Sheet": {
      "main": [
        [
          {
            "node": "Check Reply Enabled Flag",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check if Store by Date is Enabled": {
      "main": [
        [
          {
            "node": "Create Date Folder",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Set Date Folder ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check if Store by File Type is Enabled": {
      "main": [
        [
          {
            "node": "Create File Type Folder",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Set File Type Folder ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

功能特点

  • 自动检测新邮件
  • AI智能内容分析
  • 自定义分类规则
  • 批量处理能力
  • 详细的处理日志

技术分析

节点类型及作用

  • Googlesheets
  • Googledrive
  • Code
  • Merge
  • If

复杂度评估

配置难度:
★★★★☆
维护难度:
★★☆☆☆
扩展性:
★★★★☆

实施指南

前置条件

  • 有效的Gmail账户
  • n8n平台访问权限
  • Google API凭证
  • AI分类服务订阅

配置步骤

  1. 在n8n中导入工作流JSON文件
  2. 配置Gmail节点的认证信息
  3. 设置AI分类器的API密钥
  4. 自定义分类规则和标签映射
  5. 测试工作流执行
  6. 配置定时触发器(可选)

关键参数

参数名称 默认值 说明
maxEmails 50 单次处理的最大邮件数量
confidenceThreshold 0.8 分类置信度阈值
autoLabel true 是否自动添加标签

最佳实践

优化建议

  • 定期更新AI分类模型以提高准确性
  • 根据邮件量调整处理批次大小
  • 设置合理的分类置信度阈值
  • 定期清理过期的分类规则

安全注意事项

  • 妥善保管API密钥和认证信息
  • 限制工作流的访问权限
  • 定期审查处理日志
  • 启用双因素认证保护Gmail账户

性能优化

  • 使用增量处理减少重复工作
  • 缓存频繁访问的数据
  • 并行处理多个邮件分类任务
  • 监控系统资源使用情况

故障排除

常见问题

邮件未被正确分类

检查AI分类器的置信度阈值设置,适当降低阈值或更新训练数据。

Gmail认证失败

确认Google API凭证有效且具有正确的权限范围,重新进行OAuth授权。

调试技巧

  • 启用详细日志记录查看每个步骤的执行情况
  • 使用测试邮件验证分类逻辑
  • 检查网络连接和API服务状态
  • 逐步执行工作流定位问题节点

错误处理

工作流包含以下错误处理机制:

  • 网络超时自动重试(最多3次)
  • API错误记录和告警
  • 处理失败邮件的隔离机制
  • 异常情况下的回滚操作